(function(){
	var d      = document,
    p      = d.createElement('p'), // Have to hold the node (see notes)
    workerStyle = p.style, // worker style collection
//    lang   = YAHOO.lang,
    selectors = {},
    sheets = {},
    ssId   = 0,
    floatAttr = ('cssFloat' in workerStyle) ? 'cssFloat' : 'styleFloat',
    _toCssText,
    _unsetOpacity,
    _unsetProperty;

/*
 * Normalizes the removal of an assigned style for opacity.  IE uses the filter property.
 */
_unsetOpacity = ('opacity' in workerStyle) ?
    function (style) { style.opacity = ''; } :
    function (style) { style.filter = ''; };
        
/*
 * Normalizes the removal of an assigned style for a given property.  Expands
 * shortcut properties if necessary and handles the various names for the float property.
 */
workerStyle.border = "1px solid red";
workerStyle.border = ''; // IE doesn't unset child properties
_unsetProperty = workerStyle.borderLeft ?
    function (style,prop) {
        var p;
        if (prop !== floatAttr && prop.toLowerCase().indexOf('float') != -1) {
            prop = floatAttr;
        }
        if (typeof style[prop] === 'string') {
            switch (prop) {
                case 'opacity':
                case 'filter' : _unsetOpacity(style); break;
                case 'font'   :
                    style.font       = style.fontStyle = style.fontVariant =
                    style.fontWeight = style.fontSize  = style.lineHeight  =
                    style.fontFamily = '';
                    break;
                default       :
                    for (p in style) {
                        if (p.indexOf(prop) === 0) {
                            style[p] = '';
                        }
                    }
            }
        }
    } :
    function (style,prop) {
        if (prop !== floatAttr && prop.toLowerCase().indexOf('float') != -1) {
            prop = floatAttr;
        }
        if (typeof style[prop] == "string") {
            if (prop === 'opacity') {
                _unsetOpacity(style);
            } else {
                style[prop] = '';
            }
        }
    };
	
	_toCssText = function (css,base) {
	    var f = css.styleFloat || css.cssFloat || css['float'],
	        prop;
	
	    workerStyle.cssText = base || '';
	
	    if (typeof css == "string") {
	        // There is a danger here of incremental memory consumption in Opera
	        workerStyle.cssText += ';' + css;
	    } else {
	        if (f && !css[floatAttr]) {
//	            css = lang.merge(css);
	            delete css.styleFloat; delete css.cssFloat; delete css['float'];
	            css[floatAttr] = f;
	        }
	
	        for (prop in css) {
	            if (css.hasOwnProperty(prop)) {
	                try {
	                    // IE throws Invalid Value errors and doesn't like whitespace
	                    // in values ala ' red' or 'red '
	                    workerStyle[prop] = css[prop].trim();
	                }
	                catch (e) {
	                }
	            }
	        }
	    };
	    return workerStyle.cssText;
	};
	



namespace("w3c.styles.DynamicStyleSheet", {
	initialize : function(seed, name){
		var head,
	        node,
	        sheet,
	        cssRules = {},
	        _rules,
	        _insertRule,
	        _deleteRule,
	        i,r,sel;
		
		this.cssRules = cssRules;
		this.node = node;
		this.head = head;
		this._rules = _rules;
		this._insertRule=_insertRule;
		this._deleteRule = _deleteRule;
		this.i = i;
		this.r = r;
		this.sel = sel;
	
	    // Factory or constructor
	    if (!(this instanceof w3c.styles.DynamicStyleSheet)) {
	        return new w3c.styles.DynamicStyleSheet(seed,name);
	    }
	
	    // capture the DOM node if the string is an id
	    node = seed && (seed.nodeName ? seed : d.getElementById(seed));
	
	    // Check for the w3c.styles.DynamicStyleSheet in the static registry
	    if (seed && sheets[seed]) {
	        return sheets[seed];
	    } else if (node && node.yuiSSID && sheets[node.yuiSSID]) {
	        return sheets[node.yuiSSID];
	    }
	
	    // Create a style node if necessary
	    if (!node || !/^(?:style|link)$/i.test(node.nodeName)) {
	        node = d.createElement('style');
	        node.type = 'text/css';
	    }
	
	    if (typeof seed == "string") {
	        // Create entire sheet from seed cssText
	        if (seed.indexOf('{') != -1) {
	            // Not a load-time fork because low run-time impact and IE fails
	            // test for s.styleSheet at page load time (oddly)
	            if (node.styleSheet) {
	                node.styleSheet.cssText = seed;
	            } else {
	                node.appendChild(d.createTextNode(seed));
	            }
	        } else if (!name) {
	            name = seed;
	        }
	    }
	
	    if (!node.parentNode || node.parentNode.nodeName.toLowerCase() !== 'head') {
	        head = (node.ownerDocument || d).getElementsByTagName('head')[0];
	        // styleSheet isn't available on the style node in FF2 until appended
	        // to the head element.  style nodes appended to body do not affect
	        // change in Safari.
	        head.appendChild(node);
	    }
	
	    // Begin setting up private aliases to the important moving parts
	    // 1. The stylesheet object
	    // IE stores w3c.styles.DynamicStyleSheet under the "styleSheet" property
	    // Safari doesn't populate sheet for xdomain link elements
	    sheet = node.sheet || node.styleSheet;
	
	    // 2. The style rules collection
	    // IE stores the rules collection under the "rules" property
	    _rules = sheet && ('cssRules' in sheet) ? 'cssRules' : 'rules';
	
	    // 3. The method to remove a rule from the stylesheet
	    // IE supports removeRule
	    _deleteRule = ('deleteRule' in sheet) ?
	        function (i) { sheet.deleteRule(i); } :
	        function (i) { sheet.removeRule(i); };
	
	    // 4. The method to add a new rule to the stylesheet
	    // IE supports addRule with different signature
	    _insertRule = ('insertRule' in sheet) ?
	        function (sel,css,i) { sheet.insertRule(sel+' {'+css+'}',i); } :
	        function (sel,css,i) { sheet.addRule(sel,css,i); };
	
	    // 5. Initialize the cssRules map from the node
	    // xdomain link nodes forbid access to the cssRules collection, so this
	    // will throw an error.
	    // TODO: research alternate stylesheet, @media
	    for (i = sheet[_rules].length - 1; i >= 0; --i) {
	        r   = sheet[_rules][i];
	        sel = r.selectorText;
	
	        if (this.cssRules[sel]) {
	            this.cssRules[sel].style.cssText += ';' + r.style.cssText;
	            _deleteRule(i);
	        } else {
	            this.cssRules[sel] = r;
	        }
	    }
	
	    // Cache the instance by the generated Id
	    node.yuiSSID = 'yui-stylesheet-' + (ssId++);
	    w3c.styles.DynamicStyleSheet.register(node.yuiSSID,this);
	
	    // Register the instance by name if provided or defaulted from seed
	    if (name) {
	        w3c.styles.DynamicStyleSheet.register(name,this);
	    }	
	},
		
    /**
     * Get the unique yuiSSID for this w3c.styles.DynamicStyleSheet instance
     *
     * @method getId
     * @return {Number} the static id
     */
    getId : function () { return this.node.yuiSSID; },

    /**
     * The HTMLElement that this instance encapsulates
     *
     * @property node
     * @type HTMLElement
     */
    node : null,

    /**
     * Enable all the rules in the sheet
     *
     * @method enable
     * @return {w3c.styles.DynamicStyleSheet} the instance
     * @chainable
     */
    // Enabling/disabling the stylesheet.  Changes may be made to rules
    // while disabled.
    enable : function () { this.sheet.disabled = false; return this; },

    /**
     * Disable all the rules in the sheet.  Rules may be changed while the
     * w3c.styles.DynamicStyleSheet is disabled.
     *
     * @method disable
     * @return {w3c.styles.DynamicStyleSheet} the instance
     * @chainable
     */
    disable : function () { this.sheet.disabled = true; return this; },

    /**
     * Returns boolean indicating whether the w3c.styles.DynamicStyleSheet is enabled
     *
     * @method isEnabled
     * @return {Boolean} is it enabled?
     */
    isEnabled : function () { return !this.sheet.disabled; },

    /**
     * <p>Set style properties for a provided selector string.
     * If the selector includes commas, it will be split into individual
     * selectors and applied accordingly.  If the selector string does not
     * have a corresponding rule in the sheet, it will be added.</p>
     *
     * <p>The second parameter can be either a string of CSS text,
     * formatted as CSS ("font-size: 10px;"), or an object collection of
     * properties and their new values.  Object properties must be in
     * JavaScript format ({ fontSize: "10px" }).</p>
     *
     * <p>The float style property will be set by any of &quot;float&quot;,
     * &quot;styleFloat&quot;, or &quot;cssFloat&quot; if passed in the
     * object map.  Use "float: left;" format when passing a CSS text
     * string.</p>
     *
     * @method set
     * @param sel {String} the selector string to apply the changes to
     * @param css {Object|String} Object literal of style properties and
     *                      new values, or a string of cssText
     * @return {w3c.styles.DynamicStyleSheet} the w3c.styles.DynamicStyleSheet instance
     * @chainable
     */
    set : function (sel,css) {
        var rule = this.cssRules[sel],
            multi = sel.split(/\s*,\s*/),i,
            idx;

        // IE's addRule doesn't support multiple comma delimited selectors
        if (multi.length > 1) {
            for (i = multi.length - 1; i >= 0; --i) {
                this.set(multi[i], css);
            }
            return this;
        }

        // Some selector values can cause IE to hang
        if (!w3c.styles.DynamicStyleSheet.isValidSelector(sel)) {
            return this;
        }

        // Opera throws an error if there's a syntax error in assigned
        // cssText. Avoid this using a worker style collection, then
        // assigning the resulting cssText.
        if (rule) {
            rule.style.cssText = w3c.styles.DynamicStyleSheet.toCssText(css,rule.style.cssText);
        } else {
            idx = sheet[_rules].length;
            css = w3c.styles.DynamicStyleSheet.toCssText(css);

            // IE throws an error when attempting to addRule(sel,'',n)
            // which would crop up if no, or only invalid values are used
            if (css) {
                _insertRule(sel, css, idx);

                // Safari replaces the rules collection, but maintains the
                // rule instances in the new collection when rules are
                // added/removed
                this.cssRules[sel] = sheet[_rules][idx];
            }
        }
        return this;
    },

    /**
     * <p>Unset style properties for a provided selector string, removing
     * their effect from the style cascade.</p>
     *
     * <p>If the selector includes commas, it will be split into individual
     * selectors and applied accordingly.  If there are no properties
     * remaining in the rule after unsetting, the rule is removed.</p>
     *
     * <p>The style property or properties in the second parameter must be the
     * <p>JavaScript style property names. E.g. fontSize rather than font-size.</p>
     *
     * <p>The float style property will be unset by any of &quot;float&quot;,
     * &quot;styleFloat&quot;, or &quot;cssFloat&quot;.</p>
     *
     * @method unset
     * @param sel {String} the selector string to apply the changes to
     * @param css {String|Array} style property name or Array of names
     * @return {w3c.styles.DynamicStyleSheet} the w3c.styles.DynamicStyleSheet instance
     * @chainable
     */
    unset : function (sel,css) {
        var rule = this.cssRules[sel],
            multi = sel.split(/\s*,\s*/),
            remove = !css,
            rules, i;

        // IE's addRule doesn't support multiple comma delimited selectors
        // so rules are mapped internally by atomic selectors
        if (multi.length > 1) {
            for (i = multi.length - 1; i >= 0; --i) {
                this.unset(multi[i], css);
            }
            return this;
        }

        if (rule) {
            if (!remove) {
                if (!css instanceof Array) {
                    css = [css];
                }

                workerStyle.cssText = rule.style.cssText;
                for (i = css.length - 1; i >= 0; --i) {
                    _unsetProperty(workerStyle,css[i]);
                }

                if (workerStyle.cssText) {
                    rule.style.cssText = workerStyle.cssText;
                } else {
                    remove = true;
                }
            }
            
            if (remove) { // remove the rule altogether
                rules = sheet[_rules];
                for (i = rules.length - 1; i >= 0; --i) {
                    if (rules[i] === rule) {
                        delete this.cssRules[sel];
                        _deleteRule(i);
                        break;
                    }
                }
            }
        }
        return this;
    },

    /**
     * Get the current cssText for a rule or the entire sheet.  If the
     * selector param is supplied, only the cssText for that rule will be
     * returned, if found.  If the selector string targets multiple
     * selectors separated by commas, the cssText of the first rule only
     * will be returned.  If no selector string, the stylesheet's full
     * cssText will be returned.
     *
     * @method getCssText
     * @param sel {String} Selector string
     * @return {String}
     */
    getCssText : function (sel) {
        var rule,css;

        if (typeof sel == "string") {
            // IE's addRule doesn't support multiple comma delimited
            // selectors so rules are mapped internally by atomic selectors
            rule = this.cssRules[sel.split(/\s*,\s*/)[0]];

            return rule ? rule.style.cssText : null;
        } else {
            css = [];
            for (sel in this.cssRules) {
                if (this.cssRules.hasOwnProperty(sel)) {
                    rule = this.cssRules[sel];
                    css.push(rule.selectorText+" {"+rule.style.cssText+"}");
                }
            }
            return css.join("\n");
        }
	}
});


	js.extend(w3c.styles.DynamicStyleSheet, {
	    /**
	     * <p>Converts an object literal of style properties and values into a string
	     * of css text.  This can then be assigned to el.style.cssText.</p>
	     *
	     * <p>The optional second parameter is a cssText string representing the
	     * starting state of the style prior to alterations.  This is most often
	     * extracted from the eventual target's current el.style.cssText.</p>
	     *
	     * @method w3c.styles.DynamicStyleSheet.toCssText
	     * @param css {Object} object literal of style properties and values
	     * @param cssText {String} OPTIONAL starting cssText value
	     * @return {String} the resulting cssText string
	     * @static
	     */
	    toCssText : (('opacity' in workerStyle) ? _toCssText :
	        // Wrap IE's toCssText to catch opacity.  The copy/merge is to preserve
	        // the input object's integrity, but if float and opacity are set, the
	        // input will be copied twice in IE.  Is there a way to avoid this
	        // without increasing the byte count?
	        function (css, cssText) {
	            if (typeof css=="object" && 'opacity' in css) {
	                css = js.merge(css,{
	                        filter: 'alpha(opacity='+(css.opacity*100)+')'
	                      });
	                delete css.opacity;
	            }
	            return _toCssText(css,cssText);
	        }),
	
	    /**
	     * Registers a w3c.styles.DynamicStyleSheet instance in the static registry by the given name
	     *
	     * @method w3c.styles.DynamicStyleSheet.register
	     * @param name {String} the name to assign the w3c.styles.DynamicStyleSheet in the registry
	     * @param sheet {w3c.styles.DynamicStyleSheet} The w3c.styles.DynamicStyleSheet instance
	     * @return {Boolean} false if no name or sheet is not a w3c.styles.DynamicStyleSheet
	     *              instance. true otherwise.
	     * @static
	     */
	    register : function (name,sheet) {
	        return !!(name && sheet instanceof w3c.styles.DynamicStyleSheet &&
	                  !sheets[name] && (sheets[name] = sheet));
	    },
	
	    /**
	     * <p>Determines if a selector string is safe to use.  Used internally
	     * in set to prevent IE from locking up when attempting to add a rule for a
	     * &quot;bad selector&quot;.</p>
	     *
	     * <p>Bad selectors are considered to be any string containing unescaped
	     * `~!@$%^&()+=|{}[];'"?< or space. Also forbidden are . or # followed by
	     * anything other than an alphanumeric.  Additionally -abc or .-abc or
	     * #_abc or '# ' all fail.  There are likely more failure cases, so
	     * please file a bug if you encounter one.</p>
	     *
	     * @method w3c.styles.DynamicStyleSheet.isValidSelector
	     * @param sel {String} the selector string
	     * @return {Boolean}
	     * @static
	     */
	    isValidSelector : function (sel) {
	        var valid = false;
	
	        if (sel && typeof sel=="string") {
	
	            if (!selectors.hasOwnProperty(sel)) {
	                // TEST: there should be nothing but white-space left after
	                // these destructive regexs
	                selectors[sel] = !/\S/.test(
	                    // combinators
	                    sel.replace(/\s+|\s*[+~>]\s*/g,' ').
	                    // attribute selectors (contents not validated)
	                    replace(/([^ ])\[.*?\]/g,'$1').
	                    // pseudo-class|element selectors (contents of parens
	                    // such as :nth-of-type(2) or :not(...) not validated)
	                    replace(/([^ ])::?[a-z][a-z\-]+[a-z](?:\(.*?\))?/ig,'$1').
	                    // element tags
	                    replace(/(?:^| )[a-z0-6]+/ig,' ').
	                    // escaped characters
	                    replace(/\\./g,'').
	                    // class and id identifiers
	                    replace(/[.#]\w[\w\-]*/g,''));
	            }
	
	            valid = selectors[sel];
	        }
	        return valid;
	    }
	});
})();